package com.amoby.main.service.impl;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.amoby.base.commbank.commbiz.DetailRecord;
import com.amoby.base.commbank.commbiz.DirectEntryFile;
import com.amoby.base.commbank.commbiz.HeaderRecord;
import com.amoby.base.commbank.commbiz.TrailerRecord;
import com.amoby.base.commbank.reportfile.FileResolver;
import com.amoby.base.commbank.reportfile.ReportEvents;
import com.amoby.base.commbank.reportfile.TransactionGroupEvents;
import com.amoby.base.common.Constants;
import com.amoby.base.utils.DateUtils;
import com.amoby.base.utils.ftp.FTPPool;
import com.amoby.base.utils.ftp.SFTPClient;
import com.amoby.main.entity.Cbabill;
import com.amoby.main.entity.CommbankPay;
import com.amoby.main.mapper.CommbankPayMapper;
import com.amoby.main.service.BillOperation;
import com.amoby.main.service.CbaBillService;
import com.amoby.main.service.CommBankService;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.SftpATTRS;

/**
 * @date 2015-6-24 下午1:37:52
 * @author fred
 * @version V1.0
 * @Description
 * 
 */
@Service("commBankService")
public class CommBankServiceImpl implements CommBankService {
    private static final Logger logger         = LoggerFactory.getLogger(CommBankServiceImpl.class);
    /** 联邦银行 COMMBIZ 识别号 */
    private static final int    COMMBIZ_NUMBER = 301500;
    /** 联邦银行 COMMBIZ 名 */
    private static final String COMMBIZ_NAME   = "CommBiz";
    @Autowired
    private CbaBillService      cbaBillService;
    @Autowired
    private FTPPool<SFTPClient> ftpPool;
    @Autowired
    private CommbankPayMapper   commbankPayMapper;

    public CommBankServiceImpl() {
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public CommbankPay createPayment(Cbabill bill) throws Exception {
        CommbankPay commbankPay = new CommbankPay(bill);
        // 创建DE文件(支付信息文件)
        DirectEntryFile deFile = new DirectEntryFile();
        HeaderRecord header = new HeaderRecord();
        DetailRecord detail = null;
        TrailerRecord trailer = new TrailerRecord();
        header.setCommBizNumber(COMMBIZ_NUMBER);
        header.setCommBizName(COMMBIZ_NAME);
        header.setDescription(commbankPay.getPayDesc());
        header.setDate(new Date());
        deFile.setHeader(header);
        // 划出账户信息
        detail = new DetailRecord();
        detail.setDetailType(DirectEntryFile.DETAIL_TYPE_DEBIT);
        detail.setBsbNumber(commbankPay.getBsb());
        detail.setAccountNumber(commbankPay.getAcc());
        detail.setAccountTitle(commbankPay.getAccName());
        detail.setIndicator("N");
        detail.setTransactionCode("13");
        detail.setAmount(commbankPay.getAmount());
        detail.setLodgementReference(commbankPay.getLodgementRef());
        detail.setTraceBSBNumber(commbankPay.getBsb());
        detail.setTraceAccountNumber(commbankPay.getAcc());
        detail.setRemitterName(commbankPay.getRemitter());
        detail.setTax(new Double(0));
        deFile.addDetail(detail);
        // 划入账户信息
        detail = new DetailRecord();
        detail.setDetailType(DirectEntryFile.DETAIL_TYPE_CREDIT);
        detail.setBsbNumber(commbankPay.getBsBsb());
        detail.setAccountNumber(commbankPay.getBsAcc());
        detail.setAccountTitle(commbankPay.getBsName());
        detail.setIndicator("N");
        detail.setTransactionCode("50");
        detail.setAmount(commbankPay.getAmount());
        detail.setLodgementReference(commbankPay.getLodgementRef());
        detail.setTraceBSBNumber(commbankPay.getBsBsb());
        detail.setTraceAccountNumber(commbankPay.getBsAcc());
        detail.setRemitterName(commbankPay.getRemitter());
        detail.setTax(new Double(0));
        deFile.addDetail(detail);
        // Trailer Recode 的 BSB Number 缺省值为 999999
        trailer.setBsbNumber("999999");
        deFile.setTrailer(trailer);
        // 产生文件
        deFile.setFileName(String.format("%1$s_%2$s.DE", commbankPay.getLodgementRef(), DateUtils.getCurrentTimeString()));
        deFile.store(Constants.commBiz.locPath + Constants.commBiz.outBox);
        // File file = deFile.store(Constants.commBiz.locPath + Constants.commBiz.outBox);
        // 初始状态
        commbankPay.setState((byte) 1);
        commbankPay.setFileName(deFile.getFileName());
        commbankPay.setCrtime(new Date());
        // 保存到数据库
        if (commbankPayMapper.insert(commbankPay) == 0) {
            throw new RuntimeException("支付信息产生失败");
        }
        return commbankPay;
    }
    /**
     * 生成批量的转账文件
     */
	@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
	public List<CommbankPay> createPaymentBatch(List<Cbabill> cbaList) throws Exception {
		List<CommbankPay> bankPayList = new ArrayList<CommbankPay>();
		DirectEntryFile deFile = new DirectEntryFile();
		String fileName = String.format("%1$s_%2$s.DE", cbaList.get(0).getLodgementRef(), DateUtils.getCurrentTimeString());
		// 创建DE文件(支付信息文件)
		HeaderRecord header = new HeaderRecord();
		DetailRecord detail = null;
		TrailerRecord trailer = new TrailerRecord();
		header.setCommBizNumber(COMMBIZ_NUMBER);
		header.setCommBizName(COMMBIZ_NAME);
		header.setDescription("");
		header.setDate(new Date());
		deFile.setHeader(header);
		CommbankPay commbank = new CommbankPay(cbaList.get(0));
		// 划出账户信息
        detail = new DetailRecord();
        detail.setDetailType(DirectEntryFile.DETAIL_TYPE_DEBIT);
        detail.setBsbNumber(commbank.getBsb());
        detail.setAccountNumber(commbank.getAcc());
        detail.setAccountTitle(commbank.getAccName());
        detail.setIndicator("N");
        detail.setTransactionCode("13");
        detail.setAmount(commbank.getAmount());
        detail.setLodgementReference(commbank.getLodgementRef());
        detail.setTraceBSBNumber(commbank.getBsb());
        detail.setTraceAccountNumber(commbank.getAcc());
        detail.setRemitterName(commbank.getRemitter());
        detail.setTax(new Double(0));
        deFile.addDetail(detail);
		for(Cbabill bill:cbaList){
			CommbankPay commbankPay = new CommbankPay(bill);
	        // 划出账户信息
	        /*detail = new DetailRecord();
	        detail.setDetailType(DirectEntryFile.DETAIL_TYPE_DEBIT);
	        detail.setBsbNumber(commbankPay.getBsb());
	        detail.setAccountNumber(commbankPay.getAcc());
	        detail.setAccountTitle(commbankPay.getAccName());
	        detail.setIndicator("N");
	        detail.setTransactionCode("13");
	        detail.setAmount(commbankPay.getAmount());
	        detail.setLodgementReference(commbankPay.getLodgementRef());
	        detail.setTraceBSBNumber(commbankPay.getBsb());
	        detail.setTraceAccountNumber(commbankPay.getAcc());
	        detail.setRemitterName(commbankPay.getRemitter());
	        detail.setTax(new Double(0));
	        deFile.addDetail(detail);*/
	        // 划入账户信息
	        detail = new DetailRecord();
	        detail.setDetailType(DirectEntryFile.DETAIL_TYPE_CREDIT);
	        detail.setBsbNumber(commbankPay.getBsBsb());
	        detail.setAccountNumber(commbankPay.getBsAcc());
	        detail.setAccountTitle(commbankPay.getBsName());
	        detail.setIndicator("N");
	        detail.setTransactionCode("50");
	        detail.setAmount(commbankPay.getAmount());
	        detail.setLodgementReference(commbankPay.getLodgementRef());
	        detail.setTraceBSBNumber(commbankPay.getBsBsb());
	        detail.setTraceAccountNumber(commbankPay.getBsAcc());
	        detail.setRemitterName(commbankPay.getRemitter());
	        detail.setTax(new Double(0));
	        deFile.addDetail(detail);
	        // Trailer Recode 的 BSB Number 缺省值为 999999
	        trailer.setBsbNumber("999999");
	        deFile.setTrailer(trailer);
	        // 初始状态
	        commbankPay.setState((byte) 1);
	        commbankPay.setFileName(fileName);
	        commbankPay.setCrtime(new Date());
	        // 保存到数据库
	        if (commbankPayMapper.insert(commbankPay) == 0) {
	            throw new RuntimeException("支付信息产生失败");
	        }
	        bankPayList.add(commbankPay);
        }
		// 产生文件
        deFile.setFileName(fileName);
        deFile.store(Constants.commBiz.locPath + Constants.commBiz.outBox);
        // File file = deFile.store(Constants.commBiz.locPath + Constants.commBiz.outBox);
		return bankPayList;
	}

	
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void postPayment(CommbankPay commbankPay) throws Exception {
        // 修改状态
        commbankPay.setState((byte) 2);
        commbankPay.setSubmitTime(new Date());
        commbankPay.setState((byte) 3);
        if (commbankPayMapper.updateByPrimaryKey(commbankPay) != 1) {
            throw new RuntimeException("保存状态失败");
        }
        SFTPClient sftp = ftpPool.currentResource();
        try {
            String fileName = commbankPay.getFileName();
            File file = new File(Constants.commBiz.locPath + Constants.commBiz.outBox, fileName);
            logger.info("向 commbank上传支付文件 file = " + file);
            sftp.put(file, Constants.commBiz.outBox + "/" + fileName);
        } catch (Exception e) {
            throw e;
        } finally {
            if (sftp != null) ftpPool.returnResource(sftp);
        }
    }
    /**
     * 批量转账改状态
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
	public void postPaymentBatch(List<CommbankPay> commbankPayList) throws Exception{
    	// 修改状态
    	for(CommbankPay commbankPay:commbankPayList){
	        commbankPay.setState((byte) 2);
	        commbankPay.setSubmitTime(new Date());
	        commbankPay.setState((byte) 3);
	        if (commbankPayMapper.updateByPrimaryKey(commbankPay) != 1) {
	            throw new RuntimeException("保存状态失败");
	        }
    	}
        SFTPClient sftp = ftpPool.currentResource();
        try {
            String fileName = commbankPayList.get(0).getFileName();
            File file = new File(Constants.commBiz.locPath + Constants.commBiz.outBox, fileName);
            logger.info("向 commbank上传支付文件 file = " + file);
            sftp.put(file, Constants.commBiz.outBox + "/" + fileName);
        } catch (Exception e) {
            throw e;
        } finally {
            if (sftp != null) ftpPool.returnResource(sftp);
        }
	}
    /**
     * 定时同步SFTP上的input目录，将报告文件下载回来进行分析
     * 
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void checkFeedback() throws Exception {
        SFTPClient sftp = ftpPool.currentResource();
        // 从commbank检查是否有新的回授文件需要下载， 并且下载对应文件
        List<LsEntry> list = sftp.ls(Constants.commBiz.inBox);
        for (LsEntry entry : list) {
            SftpATTRS attr = entry.getAttrs();
            if (!attr.isDir() && !attr.isLink()) {
                String fileName = Constants.commBiz.inBox + "/" + entry.getFilename();
                File file = new File(Constants.commBiz.locPath + fileName);
                try {
                    if (file.exists()) {
                        logger.warn("文件已经存在，将进行内容复写 file = " + file);
                    } else {
                        FileUtils.touch(file);
                    }
                    logger.info("从 commbank下载文件 file = " + fileName);
                    sftp.get(fileName, file);
                    // 解析文件内容
                    FileResolver resolver = FileResolver.build(file.getParent(), file.getName());
                    ReportEvents events = resolver.resolver(file);
                    // 根据文件名取出CommbankPay和Cbabill信息
                    String originalFilename = events.getOriginalFilename();
                    CommbankPay commbankPay = commbankPayMapper.selectByFileName(originalFilename);
                    if (commbankPay == null) {
                        throw new RuntimeException("回授文件无法关联到CommbankPay信息 originalFilename=" + originalFilename);
                    }
                    BillOperation billOperation = null;
                    // 设置状态回授信息到数据库实体对象中
                    commbankPay.setReportFile(events.getFileName());
                    commbankPay.setStatusCode(events.getStatus());
                    if (events.isSuccess()) { // 支付成功
                        commbankPay.setState((byte) 4);
                        if (events instanceof TransactionGroupEvents) {
                            commbankPay.setProcessDate(((TransactionGroupEvents) events).getProcessingDate());
                        }
                        billOperation = BillOperation.success;
                    } else if (events.isFail()) {// 支付失败
                        commbankPay.setState((byte) 5);
                        commbankPay.setErrCode(events.getErrCode());
                        commbankPay.setErrMsg(events.getErrMsg());
                        billOperation = BillOperation.fail;
                    }
                    // 更新到数据库
                    if (commbankPayMapper.updateByPrimaryKey(commbankPay) != 1) {
                        throw new RuntimeException("保存状态失败");
                    }
                    if (billOperation != null) {
                        // 流转账单状态
                        cbaBillService.flowBill(commbankPay.getBillId(), billOperation);
                    }
                    sftp.rm(fileName);
                } catch (UnsupportedOperationException e) {
                    logger.info("回授文件格式不被支持，略过。 file = {}， e = {}", file, e.getMessage());
                    sftp.rm(fileName);// 不支持的文件格式，从服务器上删除副本
                } catch (Exception e) {
                    logger.warn("文件下载解析失败，略过 file = " + fileName, e);
                }
            }
        }
        ftpPool.returnResource(sftp);
    }

    /**
     * 获取批量转账的回援文件
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void checkFeedbackBatch() throws Exception {
        SFTPClient sftp = ftpPool.currentResource();
        // 从commbank检查是否有新的回授文件需要下载， 并且下载对应文件
        List<LsEntry> list = sftp.ls(Constants.commBiz.inBox);
        for (LsEntry entry : list) {
            SftpATTRS attr = entry.getAttrs();
            if (!attr.isDir() && !attr.isLink()) {
                String fileName = Constants.commBiz.inBox + "/" + entry.getFilename();
                File file = new File(Constants.commBiz.locPath + fileName);
                try {
                    if (file.exists()) {
                        logger.warn("文件已经存在，将进行内容复写 file = " + file);
                    } else {
                        FileUtils.touch(file);
                    }
                    logger.info("从 commbank下载文件 file = " + fileName);
                    sftp.get(fileName, file);
                    // 解析文件内容
                    FileResolver resolver = FileResolver.build(file.getParent(), file.getName());
                    ReportEvents events = resolver.resolver(file);
                    // 根据文件名取出CommbankPay和Cbabill信息
                    String originalFilename = events.getOriginalFilename();
                    List<CommbankPay> commbankPayList = commbankPayMapper.selectByFileNameList(originalFilename);
                    for(CommbankPay commbankPay:commbankPayList){
                    	if (commbankPay == null) {
                            throw new RuntimeException("回授文件无法关联到CommbankPay信息 originalFilename=" + originalFilename);
                        }
                        BillOperation billOperation = null;
                        // 设置状态回授信息到数据库实体对象中
                        commbankPay.setReportFile(events.getFileName());
                        commbankPay.setStatusCode(events.getStatus());
                        if (events.isSuccess()) { // 支付成功
                            commbankPay.setState((byte) 4);
                            if (events instanceof TransactionGroupEvents) {
                                commbankPay.setProcessDate(((TransactionGroupEvents) events).getProcessingDate());
                            }
                            billOperation = BillOperation.success;
                        } else if (events.isFail()) {// 支付失败
                            commbankPay.setState((byte) 5);
                            commbankPay.setErrCode(events.getErrCode());
                            commbankPay.setErrMsg(events.getErrMsg());
                            billOperation = BillOperation.fail;
                        }
                        // 更新到数据库
                        if (commbankPayMapper.updateByPrimaryKey(commbankPay) != 1) {
                            throw new RuntimeException("保存状态失败");
                        }
                        if (billOperation != null) {
                            // 流转账单状态
                            cbaBillService.flowBill(commbankPay.getBillId(), billOperation);
                        }
                    }
                    sftp.rm(fileName);
                } catch (UnsupportedOperationException e) {
                    logger.info("回授文件格式不被支持，略过。 file = {}， e = {}", file, e.getMessage());
                    sftp.rm(fileName);// 不支持的文件格式，从服务器上删除副本
                } catch (Exception e) {
                    logger.warn("文件下载解析失败，略过 file = " + fileName, e);
                }
            }
        }
        ftpPool.returnResource(sftp);
    }
    /**
     * 测试产生支付文件
     * 
     * @param args
     * @throws IOException
     * @throws JSchException
     */
    public static void main(String[] args) throws Exception {
        DirectEntryFile paymentFile = new DirectEntryFile();
        HeaderRecord header = new HeaderRecord();
        DetailRecord detail = null;
        TrailerRecord trailer = new TrailerRecord();
        header.setCommBizNumber(301500);
        header.setCommBizName("CommBiz");
        header.setDescription("eswipe");
        header.setDate(new Date());
        paymentFile.setHeader(header);
        detail = new DetailRecord();
        detail.setDetailType(DirectEntryFile.DETAIL_TYPE_DEBIT);
        detail.setBsbNumber("062-000");
        detail.setAccountNumber("13907636");
        detail.setAccountTitle("Beynod PTY LTD");
        detail.setIndicator("N");
        detail.setTransactionCode("13");
        detail.setAmount(new BigDecimal("1.2").setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
        detail.setLodgementReference("1415189");
        detail.setTraceBSBNumber("062-000");
        detail.setTraceAccountNumber("13907636");
        detail.setRemitterName("eswipe");
        detail.setTax(new Double("0.00"));
        paymentFile.addDetail(detail);
        detail = new DetailRecord();
        detail.setDetailType(DirectEntryFile.DETAIL_TYPE_CREDIT);
        detail.setBsbNumber("062-006");
        detail.setAccountNumber("11058208");
        detail.setAccountTitle("EmperorGarden");
        detail.setIndicator("N");
        detail.setTransactionCode("50");
        detail.setAmount(new BigDecimal("1.2").setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
        detail.setLodgementReference("1415189");
        detail.setTraceBSBNumber("062-006");
        detail.setTraceAccountNumber("11058208");
        detail.setRemitterName("eswipe");
        detail.setTax(new Double("0.00"));
        paymentFile.addDetail(detail);
        trailer.setBsbNumber("999-999");
        paymentFile.setTrailer(trailer);
        paymentFile.setFileName("1415189_20150701130333.DE");
        File file = paymentFile.store("/data");
        String content = FileUtils.readFileToString(file);
        System.out.println(content);
    }


}
